/** ****************************************************************************
@addtogroup CI2cSim
@{
*******************************************************************************/

#include "share/unios.h"
#include "simi2c.h"

#define scl(level)  (dev->scl(level))
#define sdaw(level) (dev->sdaw(level))
#define sdar()      (dev->sdar())
#define delay()		unios_us_delay(dev->delay_us)

// 产生 I2C 起始信号
// 开始信号：SCL 为高电平时，SDA 由高电平向低电平跳变，表示开始传送数据
void unidrv_simi2c_start(unidrv_simi2c_t *dev) {
	sdaw(1); delay();
	scl(1);  delay();
 	sdaw(0); delay();	// START: when CLK is high, DATA change form high to low
	scl(0);	 delay();	// 钳住 I2C 总线，准备发送或接收数据
}

// 产生 I2C 停止信号
// 结束信号：SCL 为高电平时，SDA 由低电平向高电平跳变，表示结束传送数据
void unidrv_simi2c_stop(unidrv_simi2c_t *dev) {
	scl(0);	 delay();
	sdaw(0); delay();
	scl(1);  delay();	// STOP: when CLK is high DATA change form low to high
	sdaw(1); delay();	// 发送I2C总线结束信号
}

// 产生 ACK/NACK 应答
// Master 每发送完 8bit 数据后等待 Slave 的 ACK
// 即在第 9 个 clock，若 Slave 发 ACK，SDA 会被拉低。
// 若没有 ACK，SDA 会被置高，这会引起 Master 发生 RESTART 或 STOP 流程
void unidrv_simi2c_ack(unidrv_simi2c_t *dev, bool ack) {
	sdaw(!ack); // 低电平表示 ACK 
	scl(1); delay();
	scl(0); delay();
}

bool unidrv_simi2c_tx(unidrv_simi2c_t *dev, uint8_t txd) {
	bool ack = false;
	uint8_t i;

    for(i=0; i<8; i++) {
		scl(0); 			delay();	// 拉低时钟开始数据传输
        sdaw(txd & 0x80); 	delay();	// 从高位开始传输
		scl(1); 			delay();
		scl(0); 			delay();
        txd <<= 1;			// Next bit
    }

	sdar(); delay();	// SDA 设置为输入模式
	scl(1);

	for(i=0; i<20; i++) {
		// 低电平表示有应答
		if(!sdar()) { ack = true; break; }
 	}
	scl(0); delay();
	sdaw(1);

	return ack;
}

uint8_t unidrv_simi2c_rx(unidrv_simi2c_t *dev, bool ack) {
	uint8_t ch=0;
	sdar();	// SDA 设置为输入模式

    for(uint8_t i=0; i<8; i++ ) {
        ch <<= 1;			// 从高位开始接收
        scl(1); delay();
		ch += sdar();
        scl(0); delay();
    }

	// 发回 ACK/NACK
	unidrv_simi2c_ack(dev, ack);

    return ch;
}
